using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Text;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources;
using Windows.ApplicationModel.Store;
using Windows.Phone.UI.Input;
using Windows.Storage;
using Windows.System;
using Windows.System.UserProfile;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Maps;
using Windows.UI.Xaml.Input;
using BuddyTracker.Interfaces;
using BuddyTracker.Model;
using BuddyTracker.ViewModel.Encoders;
using BuddyTracker.ViewModel.Helpers;
using BuddyTracker.ViewModel.Providers;
using BuddyTracker.Views;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using IServiceProvider = BuddyTracker.Interfaces.IServiceProvider;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.Devices.Geolocation;
using Windows.System.Threading;
using Windows.UI.ViewManagement;

namespace BuddyTracker.ViewModel
{
   
    public class MainViewModel : ViewModelBase
    {
        /// <summary>
        /// Loaded dictionary for UI.
        /// </summary>
        private readonly ResourceLoader loader;
        /// <summary>
        /// Keeps all providers in a list for iterating.
        /// </summary>
        private List<IServiceProvider> ListOfProviders;
        /// <summary>
        /// Main twitter service provider.
        /// </summary>
        private TwitterServiceProvider twitter;
        /// <summary>
        /// Current location encoder.
        /// </summary>
        private ILocationEncoder encoder;
        /// <summary>
        /// Helper for MapControl.
        /// </summary>
        private LocationHelper locationHelper;
        /// <summary>
        /// Helper for settings like automatic updates etc.
        /// </summary>
        private SettingsHelper settings;
        /// <summary>
        /// Is this the first run of the app?
        /// </summary>
        private bool _firstrun;
        /// <summary>
        /// Do i ignore async calls?
        /// </summary>
        private bool _ignoreCalls = false;

        private Geopoint _currentGeopoint;

        #region Command Property Definitions - Bound to Views
        public RelayCommand PostLocationCommand
        {
            get;
            private set;
        }
        public RelayCommand LoginTwitterCommand
        {
            get;
            private set;
        }
        public RelayCommand LogOutTwitterCommand
        {
            get;
            private set;
        }
        public RelayCommand AuthTwitterCommand
        {
            get;
            private set;
        }
        public RelayCommand OnNavigatedToCommand
        {
            get;
            private set;
        }
        public RelayCommand LoginOnNavigatedToCommand { get; private set; }
        public RelayCommand LoginOnNavigatedFromCommand
        {
            get;
            private set;
        }
        public RelayCommand<string> UnfollowCommand { get; private set; }
        public RelayCommand<KeyRoutedEventArgs> FollowCommand { get; private set; }
        public RelayCommand RefreshCommand { get; private set; }
        public RelayCommand SendMailCommand { get; private set; }
        public RelayCommand RateCommand { get; private set; }
        public RelayCommand ShareCommand { get; private set; }
        public RelayCommand OtherAppsCommand { get; private set; }
        public RelayCommand VisitCommand { get; private set; }
        public RelayCommand VisitDMICommand { get; private set; }
        public RelayCommand LanguageChangeCommand { get; private set; }
        public RelayCommand<string> ShowBuddy { get; private set; }
        #endregion

        #region Field and Properties Definitons
        
        private bool _actionsAllowed;
        public bool ActionsAllowed
        {
            get { return _actionsAllowed; }
            set 
            {
                _actionsAllowed = value; 
                RaisePropertyChanged("ActionsAllowed");
            }
        }

        private string _textlatitude;
        public string TextLatitude
        {
            get { return _textlatitude; }
            set
            {
                _textlatitude = value;
                RaisePropertyChanged("TextLatitude");
            }
        }

        private string _textlongitude;
        public string TextLongitude
        {
            get { return _textlongitude; }
            set
            {
                _textlongitude = value;
                RaisePropertyChanged("TextLongitude");
            }
        }

        private Uri _twitterloginuri;
        public Uri TwitterLoginUri
        { get { return _twitterloginuri; }
            set
            {
                _twitterloginuri = value;
                RaisePropertyChanged("TwitterLoginUri");
            }
        }

        private string _twitterpin;
        public string TwitterPin
        { get { return _twitterpin; }
            set
            {
                _twitterpin = value;
                RaisePropertyChanged("TwitterPin");
            }
        }

        private bool _loginenabled;
        public bool LoginEnabled
        {
            get { return _loginenabled; }
            set
            {
                _loginenabled = value;
                RaisePropertyChanged("LoginEnabled");
            }
        }
        
        private bool _logoutenabled;
        public bool LogoutEnabled
        {
            get { return _logoutenabled; }
            set
            {
                _logoutenabled = value;
                RaisePropertyChanged("LogoutEnabled");
            }
        }

        private bool _locationallowed = true;
        public bool LocationAllowed
        {
            get { return _locationallowed; }
            set
            {
                _locationallowed = value;
                if (!value)
                {
                    ApplicationData.Current.LocalSettings.Values["LocationConsent"] = false;
                    TextLatitude = loader.GetString("disallowed");
                    TextLongitude = loader.GetString("disallowed");
                    locationHelper.Clear();
                }
                else
                {
                    ApplicationData.Current.LocalSettings.Values["LocationConsent"] = true;
                    TextLongitude = loader.GetString("PleaseWait");
                    TextLatitude = loader.GetString("PleaseWait");
                    Refresh();
                }
                RaisePropertyChanged("LocationAllowed");
            }
        }

        private string _statusmessage;
        public string StatusMessage
        {
            get { return _statusmessage; }
            set
            {
                _statusmessage = value;
                RaisePropertyChanged("StatusMessage");
            }
        }

        private string _searchboxtext;
        public string SearchBoxText
        {
            get { return _searchboxtext; }
            set
            { 
                _searchboxtext = value;
                RaisePropertyChanged("SearchBoxText");
            }
        }

        #region settings commands

        private string _keyPhrase;
        public string KeyPhraseText
        {
            get { return _keyPhrase; }
            set
            {
                _keyPhrase = value;
                AESLocationEncoder.Masterpass = AESLocationEncoder._staticPass + value;
                RaisePropertyChanged("KeyPhraseText");
                settings.SaveSettings();
            }
        }

        private int _mapType = 0;

        public int MapType {
            get { return _mapType; }
            set
            {
                _mapType = value;
                locationHelper.SetMapStyle(_mapType);
                RaisePropertyChanged("MapType");
            } 
        }

        private int _settingLanguage;
        public int SettingLanguage
        {
            get { return _settingLanguage; }
            set
            {
                _settingLanguage = value;
                RaisePropertyChanged("SettingLanguage");
                settings.SaveSettings();
            }
        }

        private int _settingAutomaticPosting;
        public int SettingAutomaticPosting { get { return _settingAutomaticPosting; }
            set
            {
                _settingAutomaticPosting = value;
                RaisePropertyChanged("SettingAutomaticPosting");
                settings.SaveSettings();
            }
        }

        private int _settingEncoder;
        public int SettingEncoder
        {
            get { return _settingEncoder; }
            set
            {
                _settingEncoder = value;
                RaisePropertyChanged("SettingEncoder");
                if (encoder != null)
                    if (value == 0)
                    {
                        encoder = new TextLocationEncoder();
                    }
                    else if (value == 1)
                    {
                        encoder = new HexLocationEncoder();
                    }
                    else if (value == 2)
                    {
                        encoder = new AESLocationEncoder();
                    }
                settings.SaveSettings();
            }
        }

        private int _settingAutomaticPostingInterval;
        public int SettingAutomaticPostingInterval
        {
            get { return _settingAutomaticPostingInterval; }
            set
            {
                _settingAutomaticPostingInterval = value;
                RaisePropertyChanged("SettingAutomaticPostingInterval");
                settings.SaveSettings();
            }
        }

        private int _settingBuddyTrackerInterval;
        public int SettingBuddyTrackerInterval
        {
            get { return _settingBuddyTrackerInterval; }
            set
            {
                _settingBuddyTrackerInterval = value;
                RaisePropertyChanged("SettingBuddyTrackerInterval");
                settings.SaveSettings();
            }
        }
               

        #endregion

        private ObservableCollection<User> _twitterusers;
        public ObservableCollection<User> TwitterUsers
        {
            get
            {
                if (_twitterusers == null) 
                {
                    _twitterusers = new ObservableCollection<User>();
                    //populate 
                    var localSettings = ApplicationData.Current.LocalSettings;
                    if (localSettings.Values.ContainsKey("TwitterUsers"))
                    {
                        var str = localSettings.Values["TwitterUsers"] as String;
                        if (!string.IsNullOrEmpty(str))
                        {
                            var arr = str.Split(';');
                            foreach (var name in arr)
                                _twitterusers.Add(new User() { ScreenName = name });
                        }
                    }
                }

                return _twitterusers;
            }
            set
            {
                _twitterusers = value;
                RaisePropertyChanged("TwitterUsers");
            }
        }

        #endregion               

        public MainViewModel()
        {
            //setting commands to relay
            PostLocationCommand = new RelayCommand(PostLocation);
            LoginTwitterCommand = new RelayCommand(LoginTwitter);
            AuthTwitterCommand = new RelayCommand(AuthTwitter);
            LogOutTwitterCommand = new RelayCommand(LogoutTwitter);
            OnNavigatedToCommand = new RelayCommand(OnNavigatedTo);
            LoginOnNavigatedToCommand = new RelayCommand(LoginOnNavigatedTo);
            LoginOnNavigatedFromCommand = new RelayCommand(LoginOnNavigatedFrom);
            UnfollowCommand = new RelayCommand<string>(UnfollowTwitter);
            FollowCommand = new RelayCommand<KeyRoutedEventArgs>(FollowTwitter);
            RefreshCommand = new RelayCommand(Refresh);
            LanguageChangeCommand = new RelayCommand(LanguageChange);
            ShowBuddy = new RelayCommand<string>(ShowBuddyCommand);

            //string loader;
            loader = new ResourceLoader();
                        
            //setting placeholder values
            TextLatitude = loader.GetString("PleaseWait");
            TextLongitude = loader.GetString("PleaseWait");
            TwitterLoginUri = new Uri("about:blank", UriKind.Absolute);
            ListOfProviders = new List<IServiceProvider>();
            _firstrun = true;
            
            //twitter impl
            twitter = new TwitterServiceProvider();
            ActionsAllowed = twitter.IsLoggedIn();

            //settings helper
            settings = new SettingsHelper(this);

            //adding providers
            ListOfProviders.Add(twitter);

            //encoder
            if (SettingEncoder == 0)
                encoder = new TextLocationEncoder();
            else if (SettingEncoder == 1) 
                encoder = new HexLocationEncoder(); 
            else if (SettingEncoder == 2)
                encoder = new AESLocationEncoder();

            //UI stuff
            UiManipulator();
            
            //about page helper and commands
            SendMailCommand = new RelayCommand(SendEmail);
            RateCommand = new RelayCommand(RateAndReview);
            ShareCommand = new RelayCommand(Share);
            VisitCommand = new RelayCommand(VisitWebsite);
            OtherAppsCommand = new RelayCommand(ShowOtherApps);
            VisitDMICommand = new RelayCommand(VisitDMIWebsite);
                                  
        }

        /// <summary>
        /// Handler for refresh button.
        /// </summary>
        public async void Refresh()
        {
            if (_ignoreCalls)
                return;

            _ignoreCalls = true;

            StatusBarHelper.ShowIndeterminate();
            try
            {
                if (LocationAllowed)
                {
                    var l = await locationHelper.GetLocationAsync();
                    
                    if (l == null)
                    {
                        await Search();
                        StatusBarHelper.Hide();
                        return;
                    }

                    _currentGeopoint = l;

                    locationHelper.Clear();
                    TextLatitude = l.Position.Latitude.ToString("0.00000");
                    TextLongitude = l.Position.Longitude.ToString("0.00000");
                    locationHelper.ShowMyLocationCentered(l, loader.GetString("MyLocation"));
                }

                await Search();
                
                StatusBarHelper.Hide();
            }
            catch (Exception e)
            {
                MessageDialogHelper.ShowErrorMessage(e.Message, e);
            }
            finally
            {
                _ignoreCalls = false;
            }
        }
  
        /// <summary>
        /// Searches for buddies over all providers.
        /// </summary>
        public async Task Search()
        {
            foreach (var i in ListOfProviders.Where(i => i.IsLoggedIn()))
                await i.Search(TwitterUsers, locationHelper);
        }

        /// <summary>
        /// Saves twitter users to local settings as CSV since we promised we won't save locations.
        /// </summary>
        private void SaveToLocalStore()
        {
            try
            {
                var localSettings = ApplicationData.Current.LocalSettings;
                StringBuilder sb = new StringBuilder(); //faster
                foreach (User tu in TwitterUsers)
                {
                    sb.Append(tu.ScreenName);
                    sb.Append(";");
                }
                var s = sb.ToString();
                if (s.Length > 1)
                    s = s.Substring(0, s.Length - 1);//cut last ;
                localSettings.Values["TwitterUsers"] = s;
            }
            catch (Exception e)
            {
                MessageDialogHelper.ShowErrorMessage(e.Message, e);
            }
        }

        /// <summary>
        /// Posts location to all providers.
        /// </summary>
        public async void PostLocation()
        {
            if (_ignoreCalls)
                return;

            _ignoreCalls = true;

            if (_currentGeopoint == null)
                return;

            StatusBarHelper.ShowIndeterminate();
            
            try
            {
                foreach (var i in ListOfProviders)
                    await i.PostLocation(_currentGeopoint, encoder);

                StatusBarHelper.Hide();

                StatusBar statusBar = StatusBar.GetForCurrentView();
                statusBar.ProgressIndicator.ProgressValue = 0;
                statusBar.ProgressIndicator.Text = loader.GetString("MessagePostedTweet");
                await statusBar.ProgressIndicator.ShowAsync();
                
                //hide message after 2 sec
                TimeSpan period = TimeSpan.FromSeconds(2);

                ThreadPoolTimer.CreateTimer(async delegate {
                    var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
                    //on ui thread
                    await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                         StatusBarHelper.Hide();
                    });
                   
                }, period);
            }
            catch (Exception e)
            {
                MessageDialogHelper.ShowErrorMessage(loader.GetString("CommError"), e);
            }
            finally
            {
                _ignoreCalls = false;
            }
            
        }
        /// <summary>
        /// Logic for updating UI if the user is logged in.
        /// </summary>
        private void UiManipulator()
        {
            if (twitter.IsLoggedIn())
            {
                StatusMessage = loader.GetString("UserLoggedIn") + twitter.Username();
                ActionsAllowed = true;
                LogoutEnabled = true;
                LoginEnabled = false;
            }
            else
            {
                StatusMessage = loader.GetString("UserNotLoggedIn");
                ActionsAllowed = false;
                LogoutEnabled = false;
                LoginEnabled = true;
            }
        }

        /// <summary>
        /// Change language and prompt for a restart.
        /// </summary>
        public async void LanguageChange()
        {
            await MessageDialogHelper.ShowAsync(loader.GetString("AppRestart"), "Follower"); //first show the message, because it will mix languages

            switch (SettingLanguage)
            {
                case 1:
                    settings.SetLanguage("en-US");
                    break;
                case 2:
                    settings.SetLanguage("sr-Latn");
                    break;
                case 3:
                    settings.SetLanguage("sr-cyrl");
                    break;
                default: //case 0:
                    settings.SetLanguage(GlobalizationPreferences.Languages[0]); //user's #1 language
                    break;
            }
            
            Application.Current.Exit();
        }

        #region twitter
        /// <summary>
        /// Follows user on twitter, ads him to the list. This is actually KeyDown listener.
        /// </summary>
        /// <param name="obj">Event arguments.</param>
        private void FollowTwitter(KeyRoutedEventArgs obj)
        {
            if (obj.Key == VirtualKey.Enter)
            {
                var t = obj.OriginalSource as TextBox;

                #region WORKAROUND T__T
                t.IsEnabled = false; // hides the keyboard since there is no other way.
                t.IsEnabled = true;
                //Windows.UI.ViewManagement.InputPane.GetForCurrentView().TryHide(); don't work
                
                SearchBoxText = t.Text; // doesn't want to update...
                #endregion

                TwitterUsers.Add(new User() { ScreenName = SearchBoxText });
                RaisePropertyChanged("TwitterUsers");
                SearchBoxText = string.Empty;
                obj.Handled = true;
                SaveToLocalStore(); //saving list to isolated storage
            }
            obj.Handled = false;
        }

        /// <summary>
        /// Removes user from the list of twitter users.
        /// </summary>
        /// <param name="name">Screen name.</param>
        private void UnfollowTwitter(string name)
        {
            User tu = null;
            foreach (User t in TwitterUsers)
                if (t.ScreenName == name)
                    tu = t;
            TwitterUsers.Remove(tu);
            RaisePropertyChanged("TwitterUsers");
            SaveToLocalStore(); //saves updated list
        }

        /// <summary>
        /// shows user centered
        /// </summary>
        /// <param name="name">username</param>
        private void ShowBuddyCommand(string name)
        {
            User tu = null;
            foreach (User t in TwitterUsers)
                if (t.ScreenName == name)
                    tu = t;
            if (tu.LastLocation != null)
            {
                locationHelper.ShowLocationCentered(tu.LastLocation);
                MessageDialogHelper.Show(loader.GetString("BuddyFound"), "Follower");
            }
            else
            {
                MessageDialogHelper.Show(loader.GetString("BuddyNotFound"), loader.GetString("Error"));
            }
        }

        /// <summary>
        /// Logs off the user from twitter.
        /// </summary>
        private void LogoutTwitter()
        {
            if (_ignoreCalls)
                return;

            twitter.LogOff();
            ActionsAllowed = false;
            UiManipulator();
            TwitterUsers = new ObservableCollection<User>();
        }

        /// <summary>
        /// Open authentification for twitter.
        /// </summary>
        private async void AuthTwitter()
        {
            try
            {
                if (TwitterPin != null)
                {
                    await twitter.Auth(TwitterPin); //just passing
                    NavigationService.Navigate(typeof(MainPage)); //going back 
                    UiManipulator();
                }
                else
                {
                    MessageDialogHelper.ShowErrorMessage(loader.GetString("NoPIN"), new Exception(loader.GetString("NoPIN")));
                }
            }
            catch (Exception e)
            {
                MessageDialogHelper.ShowErrorMessage(loader.GetString("GeneralError"), e);
            }
        }

        /// <summary>
        /// Shows twitter login page.
        /// </summary>
        private async void LoginTwitter()
        {
            try
            {
                TwitterLoginUri = await twitter.Login();
                NavigationService.Navigate(typeof(LoginPageTwitter));
            }
            catch (Exception e)
            {
                MessageDialogHelper.ShowErrorMessage(loader.GetString("GeneralError"), e);
            }
        }


        #endregion twitter      

        #region about page stuff - boring stuff for about page handling
        private DataTransferManager _dataTransferManager;

        /// <summary>
        /// Share button handler.
        /// </summary>
        public void Share()
        {

            _dataTransferManager = DataTransferManager.GetForCurrentView();
            _dataTransferManager.DataRequested += (DataTransferManager dtm, DataRequestedEventArgs args) => //adding listener for sharing data
                {
                    args.Request.Data.Properties.Title = loader.GetString("ShareMessage");
                    args.Request.Data.SetWebLink(new Uri("http://www.windowsphone.com/en-us/store/app/follower/30b1de66-b888-4442-991c-35e1ff124ef8", UriKind.Absolute));
                };

            DataTransferManager.ShowShareUI(); //shows share data page, platform independent YAY           
        }

        /// <summary>
        /// Rate and review button handler.
        /// </summary>
        public async void RateAndReview()
        {
            var a = new Uri("ms-windows-store:reviewapp?appid=" + CurrentApp.AppId);
            await Launcher.LaunchUriAsync(a);

        }

        /// <summary>
        /// Shows mine other application in the store.
        /// </summary>
        public async void ShowOtherApps()
        {

            await Launcher.LaunchUriAsync(new Uri("ms-windows-store:search?keyword=nmilosev"));
        }
        /// <summary>
        /// send me and email...
        /// </summary>
        public async void SendEmail()
        {
            var mailto = new Uri("mailto:?to=nmilosev@live.com&subject=Follower");
            await Launcher.LaunchUriAsync(mailto);
        }

        /// <summary>
        /// Visit faculty site handler
        /// </summary>
        public async void VisitDMIWebsite()
        {
            await Launcher.LaunchUriAsync(new Uri("http://www.dmi.rs"));
        }

        /// <summary>
        /// Visit my blog handler
        /// </summary>
        public async void VisitWebsite()
        {
            await Launcher.LaunchUriAsync(new Uri("http://nmilosev.svbtle.com"));
        }
        #endregion

        #region login page stuff - status bar manipulation since I can't do it from XAML

        internal void LoginOnNavigatedTo()
        {
            StatusBarHelper.ShowForTwitter();
        }

        internal void LoginOnNavigatedFrom() 
        {
            StatusBarHelper.Hide();            
        }

        #endregion
        
        #region ugly not mvvm stuff

        public void MapHelper(MapControl map) //Method Dependancy Injection, new bing maps are terrrrrible for binding data....
        {   
            //location helper
            locationHelper = new LocationHelper(map);           
        }

        public void BackPressedHandler(object sender, BackPressedEventArgs e) //isn't an event, must override it
        {

            e.Handled = true;
            
            var f = Window.Current.Content as Frame;

            if (f.SourcePageType != typeof (MainPage))
                NavigationService.Navigate(typeof (MainPage)); //going back 
            else
            {
                settings.TryStopTimers();
                Application.Current.Exit();
            }

        }

        #endregion

        /// <summary>
        /// Event handler for MainPage OnNavigatedTo().
        /// </summary>
        internal async void OnNavigatedTo()
        {

            if (_firstrun)
            {
                //location consent
                var localSettings = ApplicationData.Current.LocalSettings;

                if (!localSettings.Values.ContainsKey("LocationConsent"))
                {
                    await MessageDialogHelper.ShowYesNoDialog(loader.GetString("LocationConsentMessage"),
                        loader.GetString("LocationConsentTitle"),
                        delegate
                        {
                            localSettings.Values["LocationConsent"] = true; //yes
                            //user accepted our terms, ready for the first run, loading default settings to localsettings
                            settings.WriteDefaults();
                            LocationAllowed = true;
                        },
                        delegate
                        {
                            LocationAllowed = false;
                            //Application.Current.Exit(); //no
                        });
                }
                else
                {
                    if ((bool) localSettings.Values["LocationConsent"])
                        LocationAllowed = true;
                    else
                        LocationAllowed = false;

                }
                settings.LoadSettings();
            }

            //Refresh(); doing it from toggle getter
        }
        /// <summary>
        /// INotifyPropertyChanged invoker - MVVMLight.
        /// </summary>
        /// <param name="propertyName">Name of the property changed.</param>
        protected override void RaisePropertyChanged(string propertyName)
        {
            base.RaisePropertyChanged(propertyName);
        }
    }
}